package com.zyy.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * DCL懒汉式
 */
public class Lazy {
    private static boolean flag = false;
    private Lazy() {
        synchronized (Lazy.class) {
            if (flag) {
                throw new RuntimeException("不要试图使用反射破坏异常");
            } else {
                flag = true;
            }
        }

    }

    /**
     * 一定要加上volatile，防止指令重排造成的问题
     */
    private volatile static Lazy lazy;

    public static Lazy getInstance() {
        if (lazy == null) {
            //加锁   双重检测锁模式的  懒汉式单例  DCL懒汉式
            synchronized (Lazy.class) {
                if (lazy == null) {
                    /**
                     * 不是一个原子性操作
                     * 1. 分配内存空间
                     * 2. 执行构造方法，初始化对象
                     * 3. 把这个对象指向这个空间
                     *
                     * 正常情况123
                     * 也可能出现132 这个时候还没有完成构造，所以需要避免指令重排，用volatile
                     */
                    lazy = new Lazy();
                }
            }
        }
        return lazy;
    }

    /*public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                Lazy instance = Lazy.getInstance();
                System.out.println(instance);
            }).start();
        }
    }*/


    public static void main(String[] args) throws Exception {
        Field flagField = Lazy.class.getDeclaredField("flag");
        /**
         * 将此对象的 accessible 标志设置为指示的布尔值。
         * 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
         * 值为 false 则指示反射的对象应该实施 Java 语言访问检查;
         * 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 ；
         *
         * 由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
         */
        flagField.setAccessible(true);

        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy lazy = declaredConstructor.newInstance();

        //这句注释的话，就会抛出异常  不要试图使用反射破坏异常
        flagField.set(lazy, false);

        Lazy lazy2 = declaredConstructor.newInstance();

        //com.zyy.singleton.Lazy@4554617c
        System.out.println(lazy);
        //com.zyy.singleton.Lazy@74a14482
        System.out.println(lazy2);
    }
}
